package org.ohos.rajawali3d.examples.examples.general;


import ohos.app.Context;
import ohos.media.image.PixelMap;
import ohos.opengl.Bitmap;
import org.ohos.rajawali3d.ResourceTable;
import org.ohos.rajawali3d.examples.examples.AExampleFragment;
import org.rajawali3d.Object3D;
import org.rajawali3d.animation.Animation;
import org.rajawali3d.animation.SplineTranslateAnimation3D;
import org.rajawali3d.cameras.ChaseCamera;
import org.rajawali3d.curves.CatmullRomCurve3D;
import org.rajawali3d.lights.DirectionalLight;
import org.rajawali3d.materials.Material;
import org.rajawali3d.materials.methods.DiffuseMethod;
import org.rajawali3d.materials.plugins.FogMaterialPlugin;
import org.rajawali3d.materials.textures.ATexture;
import org.rajawali3d.materials.textures.NormalMapTexture;
import org.rajawali3d.materials.textures.Texture;
import org.rajawali3d.math.MathUtil;
import org.rajawali3d.math.vector.Vector3;
import org.rajawali3d.terrain.SquareTerrain;
import org.rajawali3d.terrain.TerrainGenerator;

public class TerrainFragment extends AExampleFragment {

    @Override
    public AExampleRenderer createRenderer() {
        return new TerrainRenderer(getContext(), this);
    }

    public static final class TerrainRenderer extends AExampleRenderer {

        private SquareTerrain mTerrain;
        private double mLastY = 0;

        public TerrainRenderer(Context context, AExampleFragment fragment) {
            super(context, fragment);
        }

        @Override
        public void initScene() {
            getCurrentScene().setBackgroundColor(0x999999);

            //
            // -- Use a chase camera that follows and invisible ('empty') object
            //    and add fog for a nice effect.
            //

            ChaseCamera chaseCamera = new ChaseCamera(new Vector3(0, 4, -8));
            chaseCamera.setFarPlane(1000);
            getCurrentScene().replaceAndSwitchCamera(chaseCamera, 0);

            getCurrentScene().setFog(new FogMaterialPlugin.FogParams(FogMaterialPlugin.FogType.LINEAR, 0x999999, 50, 100));

            //
            // -- Load a bitmap that represents the terrain. Its color values will
            //    be used to generate heights.
            //

            PixelMap bmp = Bitmap.getPixelMapFromResource(ResourceTable.Media_terrain, mContext);

            try {
                SquareTerrain.Parameters terrainParams = SquareTerrain.createParameters(bmp);
                // -- set terrain scale
                terrainParams.setScale(4f, 54f, 4f);
                // -- the number of plane subdivisions
                terrainParams.setDivisions(128);
                // -- the number of times the textures should be repeated
                terrainParams.setTextureMult(4);
                //
                // -- Terrain colors can be set by manually specifying base, middle and
                //    top colors.
                //
                // --  terrainParams.setBasecolor(Color.argb(255, 0, 0, 0));
                //     terrainParams.setMiddleColor(Color.argb(255, 200, 200, 200));
                //     terrainParams.setUpColor(Color.argb(255, 0, 30, 0));
                //
                // -- However, for this example we'll use a bitmap
                //
                terrainParams.setColorMapBitmap(bmp);
                //
                // -- create the terrain
                //
                mTerrain = TerrainGenerator.createSquareTerrainFromBitmap(terrainParams, true);
            } catch (Exception e) {
                e.printStackTrace();
            }

            //
            // -- The bitmap won't be used anymore, so get rid of it.
            //
            bmp.release();

            DirectionalLight light = new DirectionalLight(0.2f, -1f, 0f);
            light.setPower(1f);
            getCurrentScene().addLight(light);

            //
            // -- A normal map material will give the terrain a bit more detail.
            //
            Material material = new Material();
            material.enableLighting(true);
            material.useVertexColors(true);
            material.setDiffuseMethod(new DiffuseMethod.Lambert());
            try {
                Texture groundTexture = new Texture("ground", ResourceTable.Media_ground);
                groundTexture.setInfluence(.5f);
                material.addTexture(groundTexture);
                material.addTexture(new NormalMapTexture("groundNormalMap", ResourceTable.Media_groundnor));
                material.setColorInfluence(0);
            } catch (ATexture.TextureException e) {
                e.printStackTrace();
            }

            //
            // -- Blend the texture with the vertex colors
            //
            material.setColorInfluence(.5f);

            mTerrain.setMaterial(material);

            getCurrentScene().addChild(mTerrain);

            //
            // -- The empty object that will move along a curve and that
            //    will be follow by the camera
            //
            Object3D empty = new Object3D();
            empty.setVisible(false);

            //
            // -- Tell the camera to chase the empty.
            //
            chaseCamera.setLinkedObject(empty);

            //
            // -- Create a camera path based on the terrain height
            //
            CatmullRomCurve3D cameraPath = createCameraPath();

            SplineTranslateAnimation3D anim = new SplineTranslateAnimation3D(cameraPath);
            anim.setTransformable3D(empty);
            anim.setDurationMilliseconds(60000);
            anim.setRepeatMode(Animation.RepeatMode.INFINITE);
            anim.setOrientToPath(true);
            getCurrentScene().registerAnimation(anim);
            anim.play();
        }

        private CatmullRomCurve3D createCameraPath() {
            CatmullRomCurve3D path = new CatmullRomCurve3D();

            float radius = 200;
            float degreeStep = 15;
            float distanceFromGround = 20;

            for (int i = 0; i < 360; i += degreeStep) {
                double radians = MathUtil.degreesToRadians(i);
                double x = Math.cos(radians) * Math.sin(radians)
                        * radius;
                double z = Math.sin(radians) * radius;
                double y = mTerrain.getAltitude(x, z) + distanceFromGround;

                if (i > 0)
                    y = (y + mLastY) * .5f;
                mLastY = y;

                path.addPoint(new Vector3(x, y, z));
            }
            path.isClosedCurve(true);
            return path;
        }

    }

}
